package com.gitee.wsl.doc.prosemirror.history

import com.gitee.wsl.doc.prosemirror.history.bean.HistoryState
import com.gitee.wsl.doc.prosemirror.history.bean.Item
import com.gitee.wsl.doc.prosemirror.history.ropesequence.RopeSequence
import com.gitee.wsl.doc.prosemirror.state.EditorState
import com.gitee.wsl.doc.prosemirror.plugin.PluginKey
import com.gitee.wsl.doc.prosemirror.transform.map.Mapping
import com.gitee.wsl.doc.prosemirror.transform.map.StepMap

/**
 * ProseMirror's history isn't simply a way to roll back to a previous
 * state, because ProseMirror supports applying changes without adding
 * them to the history (for example during collaboration).
 * To this end, each 'Branch' (one for the undo history and one for
 * the redo history) keeps an array of 'Items', which can optionally
 * hold a step (an actual undoable change), and always hold a position
 * map (which is needed to move changes below them to apply to the
 * current document).
 * An item that has both a step and a selection bookmark is the start
 * of an 'event' — a group of changes that will be undone or redone at
 * once. (It stores only the bookmark, since that way we don't have to
 * provide a document until the selection is actually applied, which
 * is useful when compressing.)
 */

// Used to schedule history compression
const val MAX_EMPTY_ITEMS = 500

fun RopeSequence<Item>.cutOffEvents(n: Int): RopeSequence<Item> {
    var cnt = n
    var cutPoint: Int? = null
    forEach { item, i ->
        if (item.selection != null && (cnt-- == 0)) {
            cutPoint = i
            false
        } else {
            true
        }
    }
    return slice(cutPoint!!)
}

const val DEPTH_OVERFLOW = 20


fun rangesFor(maps: List<StepMap>): List<IntRange> {
    return buildList {
        for (i in maps.size - 1 downTo 0) {
            if (this.isNotEmpty()) break
            maps[i].forEach { _, _, from, to ->
                add(from..to)
            }
        }
    }
}

fun mapRanges(ranges: List<IntRange>?, mapping: Mapping): List<IntRange>? {
    if (ranges.isNullOrEmpty()) return null
    return buildList {
        ranges.forEach { range ->
            val from = mapping.map(range.first, 1)
            val to = mapping.map(range.last, -1)
            if (from <= to) add(from..to)
        }
    }
}


/** Check whether any plugin in the given state has a
// `historyPreserveItems` property in its spec, in which case we must
// preserve steps exactly as they came in, so that they can be
// rebased.
// Since this maintains a reference to plugins, the cachedPreserveItems/cachedPreserveItemsPlugins data is stored in
// PMEditorState to avoid memory leaks if they were kept static here
*/
@Suppress("NestedBlockDepth")
fun EditorState.mustPreserveItems(): Boolean = with(this) {
    if (cachedPreserveItemsPlugins != plugins) {
        cachedPreserveItems = false
        cachedPreserveItemsPlugins = plugins
        for (plugin in plugins) {
            if (plugin.spec.additionalProps["historyPreserveItems"] == true) {
                cachedPreserveItems = true
                break
            }
        }
    }
    return cachedPreserveItems
}

val historyKey = PluginKey<HistoryState>("history")
val closeHistoryKey = PluginKey<HistoryState>("closeHistory")


///** The amount of undoable events available in a given state. */
//fun undoDepth(state: PMEditorState): Int {
//    val hist = historyKey.getState(state)
//    return hist?.done?.eventCount ?: 0
//}
//
///** The amount of redoable events available in a given editor state. */
//fun redoDepth(state: PMEditorState): Int {
//    val hist = historyKey.getState(state)
//    return hist?.undone?.eventCount ?: 0
//}
/** The amount of redoable events available in a given editor state. */
fun EditorState.redoDepth(): Int {
    val hist = historyKey.getState(this)
    return hist?.undone?.eventCount ?: 0
}

/** The amount of undoable events available in a given state. */
fun EditorState.undoDepth(): Int {
    val hist = historyKey.getState(this)
    return hist?.done?.eventCount ?: 0
}